package com.hzy.admin.filter;

import com.hzy.admin.configs.AppConfigs;
import com.hzy.admin.services.system.ISysAppLogService;
import com.hzy.admin.services.system.ISysUserService;
import com.hzy.admin.repository.entity.system.SysAppLog;
import com.hzy.admin.repository.entity.system.SysUser;
import com.hzy.framework.ApiResult;
import com.hzy.framework.HttpRequestWrapper;
import com.hzy.framework.JwtTokenUtil;
import com.hzy.toolkit.Tools;
import com.hzy.framework.annotation.SystemAuthority;
import com.hzy.framework.annotation.UserAuthorization;
import com.alibaba.fastjson.JSON;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * <p>
 * 请求 过滤器
 * </p>
 *
 * @author hzy
 * @since 2020-04-26
 */
public class FilterHandlerInterceptor implements HandlerInterceptor {
    private final String logOutMessage = "未授权，请登陆授权！";
    private final String logOutPath = "/admin/login";
    private final ISysUserService sysUserService;
    private final ISysAppLogService sysAppLogService;
    private final AppConfigs appConfigs;

    public FilterHandlerInterceptor(ISysUserService sysUserService, ISysAppLogService sysAppLogService, AppConfigs appConfigs) {
        this.sysUserService = sysUserService;
        this.sysAppLogService = sysAppLogService;
        this.appConfigs = appConfigs;
    }

    /**
     * 处理 HandlerAdapte 业务 之前调用
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    @ResponseBody
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 判断是否 java 对象
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        // 获取 授权 注解
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 先找 类 中 是否有 注解
        Class<?> classMethod = method.getDeclaringClass();
        UserAuthorization userAuthorization = classMethod.getAnnotation(UserAuthorization.class);
        // 如果 类 上面 没有 注解 则 函数体上面找
        if (Objects.isNull(userAuthorization)) {
            userAuthorization = method.getAnnotation(UserAuthorization.class);
        }
        // 如果添加了 UserAuthorization 注解 并且 value = true 表示忽略
        if (Objects.nonNull(userAuthorization) && !userAuthorization.value()) {
            return this.checkAuth(request, response, handler);
        }
        // 判断 函数是否有 权限检查 注解
        SystemAuthority systemAuthority = method.getAnnotation(SystemAuthority.class);
        if (!Objects.isNull(systemAuthority)) {
            Map<String, Boolean> maps = this.getPowerMaps(handlerMethod);
            if (maps != null && systemAuthority != null) {
                String value = systemAuthority.value();
                if (StringUtils.isEmpty(value)) {
                    return true;
                }
                Optional<Map.Entry<String, Boolean>> map = maps.entrySet().stream().filter(w -> w.getKey().equals(value)).findFirst();
                if (map.isPresent() && !map.get().getValue()) {
                    Tools.contentResult(response, new StringBuilder("无权访问!"), "text/html; charset=utf-8");
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * 处理 HandlerAdapte 业务 之后调用
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        // 判断是否 java 对象
        if (!(handler instanceof HandlerMethod)) {
            return;
        }

        if (modelAndView != null) {
            // 将 页面 Title 返回
            modelAndView.addObject("title", this.appConfigs.title);
            // 判断路径path中有没有isfindback参数
            String isfindbackKeyName = "findback";
            String queryString = request.getQueryString();
            Map<String,Object> queryMap = Tools.getQueryStringMap(queryString);
            if (queryMap.containsKey(isfindbackKeyName)) {
                modelAndView.addObject(isfindbackKeyName, queryMap.get(isfindbackKeyName));
            }
        }

        // 获取 授权 注解
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        if (Objects.isNull(method)) {
            return;
        }

        SystemAuthority hzyCheckPower = method.getAnnotation(SystemAuthority.class);
        if (Objects.isNull(hzyCheckPower)) {
            return;
        }

        Map<String,Boolean> maps = this.getPowerMaps(handlerMethod);
        if (maps == null) {
            return;
        }

        modelAndView.addObject("power", JSON.toJSONString(maps));
    }

    /**
     * 请求处理 业务 完成后回调
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

        // 判断是否 java 对象
        if (!(handler instanceof HandlerMethod)) {
            return;
        }

        //判断是否记录操作日志
        if (!this.appConfigs.dbLog) {
            return;
        }

        if ("/error".equals(request.getServletPath())) {
            return;
        }

        /**
         * 客户端是以UTF-8编码传输数据到服务器端的，所以需要设置服务器端以UTF-8的编码进行接收，否则对于中文数据就会产生乱码
         */
        request.setCharacterEncoding("UTF-8");
        String ip = Tools.getRemoteIp(request);
        String api = request.getServletPath();
        String form = JSON.toJSONString(request.getParameterMap());
        String queryString = request.getQueryString();
        String formBody = "";

        if (request instanceof HttpRequestWrapper) {
            HttpRequestWrapper httpRequest = ((HttpRequestWrapper) request);
            formBody = Tools.getBodyString(httpRequest);
        }

        SysAppLog model = new SysAppLog();
        model.setIp(ip);
        model.setApi(api);
        model.setForm(form);
        model.setQueryString(queryString);
        model.setFormBody(formBody);

        this.sysAppLogService.insertOrUpdate(model);
    }

    /**
     * 授权 检查
     *
     * @param request
     * @param response
     * @param handler
     * @return
     */
    private boolean checkAuth(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 验证 token
        String tokenValue = this.sysUserService.getToken();
        if (Strings.isEmpty(tokenValue)) {
            this.result(request, response);
            return false;
        }

        // 验证 token 是否有效
        SysUser sysUser = JwtTokenUtil.unsign(tokenValue, SysUser.class);

        if (sysUser == null) {
            this.result(request, response);
            return false;
        }

        this.sysUserService.getUserInfo();

        return true;
    }

    /**
     * 获取 权限 Map
     *
     * @param handlerMethod
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    @SuppressWarnings("unchecked")
    private Map<String, Boolean> getPowerMaps(HandlerMethod handlerMethod)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object bean = handlerMethod.getBean();
        Class<?> clazz = bean.getClass();
        if (Arrays.stream(clazz.getMethods()).noneMatch(w -> "getPower".equals(w.getName()))) {
            return null;
        }
        Method getPowerMethod = clazz.getMethod("getPower");
        // 方法取消java语言检查
//        getPowerMethod.setAccessible(true);
        return (Map<String, Boolean>) getPowerMethod.invoke(bean);
    }

    /**
     * 返回数据
     *
     * @param request
     * @param response
     * @throws Exception
     */
    private void result(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 如果是 ajax
        if (Tools.isAjaxRequest(request)) {
            ApiResult data = ApiResult.result(ApiResult.ApiStatusCodeEnum.UnAuth, logOutMessage, logOutPath);
            Tools.jsonResult(response, data);
            return;
        }

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("" + "<script type='text/javascript'>" + "   alert(' " + logOutMessage + " ');"
                + "   top.window.location='" + logOutPath + "';" + "</script>" + "");
        Tools.contentResult(response, stringBuilder);
    }

}
